package main

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"log"
	"os"
	"strings"
	"time"
)

type Interface1 interface {
	M1()
	cancel(remoteFromParent bool, err error)
}

type Interface2 interface {
	M1(string)
	M2()
}

type Interface0 interface {
	// 在Go的接口类型的方法集合中放入首字母小写的非导出方法也是合法的
	cancel(remoteFromParent bool, err error)
	Done() <-chan struct{}
}

type Interface3 interface {
	Interface1
	// Interface2 // duplicate method M1
	Interface0 // ok
	M3()
}

// 方法集合为空的接口类型为空接口类型, 通常不需要自己显示定义这类空接口类型, 直接使用字面值 interface{} 这个类型字面值作为所有空接口类型的代表即可
type EmptyInterface interface{}

type QuackableAnimal interface {
	Quack()
}

type Duck struct{}

func (Duck) Quack() {
	println("duck quack!")
}

type Dog struct{}

func (Dog) Quack() {
	println("dog quack!")
}

type Bird struct{}

func (Bird) Quack() {
	println("bird quack!")
}

func AnimalQuackInForest(a QuackableAnimal) {
	a.Quack()
}

type MyError struct {
	error
}

var ErrBad = MyError{
	error: errors.New("bad things happened"),
}

func bad() bool {
	return false
}

func returnsError() error {
	var p *MyError = nil // 类型信息不为空
	if bad() {
		p = &ErrBad
	}
	return p
}

func CapReader(r io.Reader) io.Reader {
	return &capitalizedReader{r: r}
}

type capitalizedReader struct {
	r io.Reader
}

func (r *capitalizedReader) Read(p []byte) (int, error) {
	n, err := r.r.Read(p)
	if err != nil {
		return 0, err
	}

	q := bytes.ToUpper(p)
	for i, v := range q {
		p[i] = v
	}
	return n, err
}

func main() {
	// 接口类型变量, 如果没有显式赋予初值, 接口类型变量的默认值为nil.
	// Go规定: 如果一个类型T的方法集合是某接口类型I的方法的等价集合或超集, 就可以说类型T实现了接口类型I, 那么类型T得变量就可以作为合法的右值
	// 赋值给接口类型I的变量

	// 空接口类型的这一可接受任意变量变量值作为右值的特性, 使其成为Go加入泛型语法之前唯一一种具有"泛型"能力的语法元素
	var e interface{} = 15 // ok
	e = "hello, golang"    // ok
	fmt.Println(e)
	type T struct{}
	var t T
	e = t  // ok
	e = &t // ok

	// Go 语言还支持接口类型变量赋值的"逆操作", 也就是通过接口类型变量"还原"它的右值的类型与值信息, 这个过程被称为"类型断言(Type Assertion)"
	v, ok := e.(*T) // <=>
	// v := e.(*T)
	// 其中e是某一个接口类型变量, 如果T时一个非接口类型且T是想要还原的类型. 那么这句代码的含义就是断言存储在接口类型变量e中的值的类型为T
	if !ok {
		panic("e 不是 T类型变量")
	}
	fmt.Println(v)

	// 尽量定义小接口
	// 隐式契约, 无需签署, 自动生效
	// 更倾向于"小契约", 尽量定义小接口, 即方法个数在1～3个之间的接口
	// “接口越大, 抽象程度越弱”

	// 会飞的
	type Flyable interface {
		Fly()
	}

	// 会游泳的
	type Swimable interface {
		Swim()
	}

	// 会飞且会游泳的
	type FlySwimable interface {
		Flyable
		Swimable
	}

	// 小接口抽象程度更高
	// 小接口易于实现和测试
	// 小接口表示的"契约"职责单一, 易于复用组合

	// var e error = 1 // cannot use 1 (type int) as type error in assignment
	// 接口的静态属性 意味着编译器会在编译阶段对所有接口类型变量的赋值操作进行类型检查。
	// 接口的动态特性 体现在接口类型变量在运行时还存储了右值的真实类型信息，这个右值的真实类型被称为接口类型变量的动态类型。

	// 鸭子类型: 指类型所表现出的特性，不是由其基因决定的，而是由类型所表现的行为决定的。
	animals := []QuackableAnimal{new(Duck), new(Dog), new(Bird)}
	for _, animal := range animals {
		AnimalQuackInForest(animal)
	}

	err := returnsError()
	if err != nil {
		fmt.Printf("error occur: %+v\n", err)
		// return
	}
	fmt.Println("ok")
	time.Sleep(7000)

	// 如果C++和Java是关于类型层次结构和类型分类的语言，那么Go则是关于组合的语言。
	// 组合是Go语言的重要设计哲学之一，而正交性则为组合哲学的落地提供了更为方便的条件。
	// 在计算机技术中，正交性用于表示某种不相依赖性或是解耦性。
	// 编程语言的语法元素间和语言特性也存在着正交的情况，并且通过将这些正交的特性组合起来，可以实现更为高级的特性。
	/**
	 * Go语言无类型体系(Type Hierarchy), 没有父子类的概念，类型定义是正交独立的；
	 * 方法和类型是正交的，每种类型都可以拥有自己的方法集合，方法本质上只是一个讲receiver参数作为第一个参数的函数而已；
	 * 接口与它的实现者之间无“显式关联”，也就是说接口与Go语言其他部分也是正交的；
	 **/

	// 创建模式
	// 接受接口，返回结构体。 这其实就是一种把接口作为“关节”的应用模式。

	// 包装器模式
	// 在基本模式的基础上，当返回值的类型与参数类型相同时。
	// 可以实现对输入参数的类型的包装，并在不改变被包装类型（输入参数类型）的定义的情况下，返回具备新功能特性的、实现相同接口类型的新类型。
	r := strings.NewReader("hello, gopher!\n")
	lr := io.LimitReader(r, 4)
	if _, err := io.Copy(os.Stdout, lr); err != nil {
		log.Fatal(err)
	}

	r = strings.NewReader("hello, gopher!\n")
	lr = io.LimitReader(r, 4)
	lr0 := CapReader(lr)
	if _, err := io.Copy(os.Stdout, lr0); err != nil {
		log.Fatal(err)
	}

	// 适配器模式
	// 适配器模式的核心是适配器函数类型(Adapter Function Type)。适配器函数类型是一个辅助水平组合的“工具”类型。
	// 它是一个类型。它可以将一个满足特定函数前面的普通函数，显式转换成自身类型的实例，转换后的实例同时也是某个接口类型的实现者

	// 中间件
	// 在Go Web编程中，“中间件”常常指的是一个实现了http.Handler接口的http.HandlerFunc类型实例
	// 实质上，中间件就是包装模式和适配器模式结合的产物
}
